
`include "common_header.verilog" 

//  *************************************************************************
//  File : r_fifo_opt_12456e
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited. 
//  Copyright (c) 2002-2003-2004 Morethanip
//  An der Steinernen Brueke 1, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Peter Reidl
//  info@morethanip.com
//  *************************************************************************
//  Decription : Programmable FIFO with Section Flags
//  Version    : $Id: r_fifo_opt_12456e.v,v 1.7 2016/08/02 17:16:07 dk Exp $
//  *************************************************************************
// 
//    FIFO Name Coding:
//    ----------------
// 
//       1:      Section Available
//       2:      Section Empty
//       3:      Full
//       4:      Almost Full
//       5       Empty
//       6:      Almost Empty
// 
//  *************************************************************************

module r_fifo_opt_12456e (

   reset_wclk,
   reset_rclk,
   sw_reset_wclk,
   sw_reset_rclk,
   wclk,
   wren,
   din,
   rclk,
   rden,
   dout,
   dout_err,
   section,
   sect_e,
   af_level,
   ae_level,
   sav,
   septy,
   afull,
   aempty,
   empty

  `ifdef FFMEM_EXTERNAL
   ,      
   m_raddr,
   m_rden,
   m_rdata,
   m_recc,
   m_wren,
   m_waddr,
   m_wdata 
  `endif

   );
   
parameter FF_WIDTH = 32;
parameter DEPTH = 128;
parameter ADDR_WIDTH = 7;

input   reset_wclk;                     //  Asynchronous Reset - wclk Domain
input   reset_rclk;                     //  Asynchronous Reset - rclk Domain 
input   sw_reset_wclk;                  //  Synchronous Reset - wclk Domain
input   sw_reset_rclk;                  //  Synchronous Reset - rclk Domain  
input   wclk;                           //  Write Clock
input   wren;                           //  Write Enable
input   [FF_WIDTH - 1:0] din;           //  Write Data
input   rclk;                           //  Read Clock
input   rden;                           //  Read Enable
output  [FF_WIDTH - 1:0] dout;          //  Read Data
output  dout_err;                       //  Read data error (e.g. memory bit error from ECC check)
input   [ADDR_WIDTH - 1:0] section;     //  Section Threshold used to generate SAV (read clock domain)
input   [ADDR_WIDTH - 1:0] sect_e;      //  Section Threshold used to generate SEPTY (write clock domain)
input   [ADDR_WIDTH - 1:0] af_level;    //  Almost Full Threshold
input   [ADDR_WIDTH - 1:0] ae_level;    //  Almost Empty Threshold
output  sav;                            //  Packet Section Available
output  septy;                          //  Packet Section Empty                
output  afull;                          //  Set to '1' when FIFO is Almost Full
output  aempty;                         //  Set to '1' when FIFO is Almost Empty
output  empty;                          //  FIFO empty

`ifdef FFMEM_EXTERNAL        // External Memories
        
  output [ADDR_WIDTH-1:0] m_raddr;      // Read address (rclk)   
  output                  m_rden;       // Read enable (rclk)    
  input  [FF_WIDTH-1:0]   m_rdata;      // Read data (rclk)         
  input                   m_recc;       // Read data ecc error (rclk)
  output                  m_wren;       // write (wclk)       
  output [ADDR_WIDTH-1:0] m_waddr;      // write addres (wclk)
  output [FF_WIDTH-1:0]   m_wdata;      // write data (wclk)  

  wire  [ADDR_WIDTH-1:0]  m_raddr;
  wire                    m_rden; 
  wire                    m_wren; 
  wire  [ADDR_WIDTH-1:0]  m_waddr;
  wire  [FF_WIDTH-1:0]    m_wdata;

`endif

wire    [FF_WIDTH - 1:0] dout; 
wire    dout_err;
wire    sav; 
wire    septy; 
wire    afull; 
wire    aempty; 
wire    empty; 
wire    [ADDR_WIDTH - 1:0] wr_b_ptr; 
wire    [ADDR_WIDTH - 1:0] wr_g_ptr; 
wire    [ADDR_WIDTH - 1:0] rd_g_wptr;
reg     [ADDR_WIDTH - 1:0] rd_b_wptr; 
wire    [ADDR_WIDTH - 1:0] ptr_wck_diff; 
wire    [ADDR_WIDTH - 1:0] rd_b_ptr; 
wire    [ADDR_WIDTH - 1:0] rd_g_ptr; 
wire    [ADDR_WIDTH - 1:0] wr_g_rptr;
reg     [ADDR_WIDTH - 1:0] wr_b_rptr; 
wire    [ADDR_WIDTH - 1:0] ptr_rck_diff; 
reg     afull_flag; 
reg     aempty_flag; 
reg     sav_flag; 
reg     septy_flag; 
reg     empty_flag; 
wire    [ADDR_WIDTH - 1:0]  ff_rd_binval; 
wire    [ADDR_WIDTH - 1:0]  ff_wr_binval; 

parameter [ADDR_WIDTH-1:0] RFF_12456_HEXFF = {ADDR_WIDTH{1'b 1}};
parameter [ADDR_WIDTH-1:0] RFF_12456_HEX0  = {ADDR_WIDTH{1'b 0}};
parameter [ADDR_WIDTH-1:0] RFF_12456_HEX1  = {{ADDR_WIDTH-1{1'b 0}}, 1'b1} ;

`ifdef FFMEM_EXTERNAL        // External Memories

    assign m_raddr = rd_b_ptr;  // Read address (rclk)   
    assign m_rden  = rden;      // read enable (rclk)    
    assign dout      = m_rdata; // Read data (rclk)
    assign dout_err  = m_recc;  // unmasked! (rclk)
    
    assign m_wren  = wren;      // write (wclk)       
    assign m_waddr = wr_b_ptr;  // write address (wclk)
    assign m_wdata = din;       // write data (wclk)  
  
`else

        assign dout_err = 1'b 0;        // no memory errors
        
        sdpm_gen #(FF_WIDTH,DEPTH,ADDR_WIDTH) U_RAM (

                  .wad(wr_b_ptr),
                  .din(din),
                  .wclk(wclk),
                  .rclk(rclk),
                  .wren(wren),
                `ifdef MTIPSDPM_GEN_READEN
                  .rden(rden),
                `endif   
                  .rad(rd_b_ptr),
                  .dout(dout));
        
`endif


rgray_cnt #(ADDR_WIDTH,DEPTH) U_WRT (

          .clk(wclk),
          .reset(reset_wclk),
          .sw_reset(sw_reset_wclk),
          .enable(wren),
          .b_out(wr_b_ptr),
          .g_out(wr_g_ptr));

rgray_cnt #(ADDR_WIDTH,DEPTH) U_RD (

          .clk(rclk),
          .reset(reset_rclk),
          .sw_reset(sw_reset_rclk),
          .enable(rden),
          .b_out(rd_b_ptr),
          .g_out(rd_g_ptr));

assign ff_rd_binval[ADDR_WIDTH-1]   = rd_g_wptr[ADDR_WIDTH-1] ;
assign ff_rd_binval[ADDR_WIDTH-2:0] = gray2bin(rd_g_wptr) ;

mtip_xsync #(ADDR_WIDTH) U_WSYNC (

   .data_in(rd_g_ptr),
   .reset(reset_wclk),
   .clk(wclk),
   .data_s(rd_g_wptr));

always @(posedge wclk or posedge reset_wclk)
   begin : ff_rd
   if (reset_wclk == 1'b 1)
      begin
      rd_b_wptr <= {ADDR_WIDTH{1'b 0}};	
      end
   else
      begin
      if (sw_reset_wclk==1'b1)
         begin
         rd_b_wptr <= {ADDR_WIDTH{1'b 0}};
         end
      else
         begin
         rd_b_wptr <= ff_rd_binval;	
         end

      end
   end
   
//  ----------------------------------------------------------------- //
//  Write Clock Domain Path for Full/High Water Mark Generation       ///
//  Read Pointer Gray coded value is latched with write clock         //
//  to minimize metastability and since only one bit will change      //
//  the previous value will remain or increment only by one           //
//  ----------------------------------------------------------------- //

always @(posedge wclk or posedge reset_wclk)
   begin : ff_aff
   if (reset_wclk == 1'b 1)
      begin
      afull_flag <= 1'b 0;	
      end
   else
      begin
      if (sw_reset_wclk==1'b1)
         begin
         afull_flag <= 1'b 0;
         end
      else
         begin
         if (af_level == RFF_12456_HEX0)
            begin
            afull_flag <= 1'b 0;	
            end
         else if (ptr_wck_diff >= (RFF_12456_HEXFF - af_level))
         begin
            afull_flag <= 1'b 1;	
            end
         else
            begin
            afull_flag <= 1'b 0;	
            end
         end
      end
   end

assign afull = afull_flag; 

//  Free Packet Section Available Flag Generation
//  ---------------------------------------------

always @(posedge wclk or posedge reset_wclk)
   begin : ff_smpty
   if (reset_wclk == 1'b 1)
      begin
      septy_flag <= 1'b 1;	
      end
   else
      begin
      if (sw_reset_wclk==1'b1)
         begin
         septy_flag <= 1'b 1;
         end
      else
         begin
         if (sect_e == RFF_12456_HEX0)
            begin
            septy_flag <= 1'b 1;	
            end
         else if (ptr_wck_diff >= sect_e )
            begin
            septy_flag <= 1'b 0;	
            end
         else
            begin
            septy_flag <= 1'b 1;	
            end
         end
      end
   end

assign septy = septy_flag; 

//  ----------------------------------------------------------------- --

assign ff_wr_binval[ADDR_WIDTH-1]   = wr_g_rptr[ADDR_WIDTH-1] ;
assign ff_wr_binval[ADDR_WIDTH-2:0] = gray2bin(wr_g_rptr) ;

mtip_xsync #(ADDR_WIDTH) U_RSYNC (

   .data_in(wr_g_ptr),
   .reset(reset_rclk),
   .clk(rclk),
   .data_s(wr_g_rptr));

always @(posedge rclk or posedge reset_rclk)
   begin : ff_wr
   if (reset_rclk == 1'b 1)
      begin
      wr_b_rptr <= {ADDR_WIDTH{1'b 0}};	
      end
   else
      begin
      if (sw_reset_rclk==1'b1)
         begin
         wr_b_rptr     <= {ADDR_WIDTH{1'b 0}};
         end
      else
         begin
         wr_b_rptr     <= ff_wr_binval;	
         end

      end
   end

//  Asynchronous FIFO
//  -----------------

always @(posedge rclk or posedge reset_rclk)
   begin : aff_aef
   if (reset_rclk == 1'b 1)
      begin
      aempty_flag <= 1'b 1;	
      end
   else
      begin
      if (sw_reset_rclk==1'b1)
         begin
         aempty_flag <= 1'b 1;
         end
      else
         begin
         if (ae_level == RFF_12456_HEX0)
            begin
            aempty_flag <= 1'b 0;	
            end
         else if (ptr_rck_diff < ae_level)
            begin
            aempty_flag <= 1'b 1;	
            end
         else
            begin
            aempty_flag <= 1'b 0;	
            end
         end
      end
   end

assign aempty = aempty_flag; 

always @(posedge rclk or posedge reset_rclk)
   begin : ff_ef
   if (reset_rclk == 1'b 1)
      begin
      empty_flag <= 1'b 1;	
      end
   else
      begin
      if (sw_reset_rclk==1'b1)
         begin
         empty_flag <= 1'b 1;
         end
      else
         begin
         if ((rden == 1'b 1 & ptr_rck_diff == RFF_12456_HEX1) |
             (ptr_rck_diff==RFF_12456_HEX0))
            begin
            empty_flag <= 1'b 1;	
            end
         else
            begin
            empty_flag <= 1'b 0;	
            end
         end
      end
   end

assign empty = empty_flag; 

//  Packet Section In FIFO Flag Generation
//  --------------------------------------

always @(posedge rclk or posedge reset_rclk)
   begin : ff_sav
   if (reset_rclk == 1'b 1)
      begin
      sav_flag <= 1'b 0;	
      end
   else
      begin
      if (sw_reset_rclk==1'b1)
         begin
         sav_flag <= 1'b 0;
         end
      else
         begin
         if (section == RFF_12456_HEX0)
            begin
            sav_flag <= 1'b 0;	
            end
         else if (ptr_rck_diff <= section )
            begin
            sav_flag <= 1'b 0;	
            end
         else
            begin
            sav_flag <= 1'b 1;	
            end
         end
      end
   end

assign sav = sav_flag; 

assign ptr_wck_diff = wr_b_ptr - rd_b_wptr;	

assign ptr_rck_diff = wr_b_rptr - rd_b_ptr;	


// Gray to Binary Conversion
// -------------------------

function [ADDR_WIDTH-2:0] gray2bin;

        input [ADDR_WIDTH-1:0]  gray_val ;
        
        integer LOOP_IDX1; 
        integer LOOP_IDX2;
                
        for (LOOP_IDX1 = 0; LOOP_IDX1 <= ADDR_WIDTH - 2; LOOP_IDX1 = LOOP_IDX1 + 1)
        begin
         
                gray2bin[LOOP_IDX1] = gray_val[LOOP_IDX1];	

                for (LOOP_IDX2 = ADDR_WIDTH - 1; LOOP_IDX2 >= LOOP_IDX1 + 1; LOOP_IDX2 = LOOP_IDX2 - 1)
                begin
            
                        gray2bin[LOOP_IDX1] = gray2bin[LOOP_IDX1] ^ gray_val[LOOP_IDX2];	
            
                end
                
        end
        
endfunction

endmodule // module a_fifo_opt_1246